﻿//
// Author: Ruxo Zheng (http://ruxozheng.spaces.live.com/)
//
// This file is distributed under CPOL 1.0 License (http://www.codeproject.com/info/cpol10.aspx).
//

using System;

namespace RZ.Web
{
    sealed class HtmlLegacyLexer : HtmlLexer
    {
        const char OpenTag = '<';
        const char CloseTag = '>';
        const char CloseElementSign = '/';

        public HtmlLegacyLexer(String content)
            : base(content)
        {
        }

        public Boolean GetBeginOpenTag(out String tagName)
        {
            var saveCursor = this.cursor;

            SkipWhiteSpaces();

            if (EOC || Read() != OpenTag)
                goto Failed;

            if (GetTagName(out tagName))
                return true;

        Failed:
            tagName = null;
            this.cursor = saveCursor;
            return false;
        }

        public Boolean GetBeginCloseTag(out String tagName)
        {
            var saveCursor = this.cursor;

            if (!ValidLength(2) || L(0) != OpenTag || L(1) != CloseElementSign)
                goto Failed;

            this.cursor += 2;

            if (GetTagName(out tagName))
                return true;

        Failed:
            this.cursor = saveCursor;
            tagName = null;
            return false;
        }

        public Boolean GetQuoteText(out String text)
        {
            if (EOC)
            {
                text = null;
                return false;
            }

            var saveCursor = this.cursor;

            Char openQuote = L(0);

            if (openQuote == '\'' || openQuote == '"')
            {
                Int32 textStart = ++this.cursor;

                while (!EOC && CurrentChar != openQuote)
                    ++this.cursor;

                text = GetContent(textStart);

                ++this.cursor;

                return true;
            }
            else
            {
                text = null;
                this.cursor = saveCursor;
                return false;
            }
        }

        public Boolean GetSingleCompromiseWord(out String text)
        {
            var textStart = this.cursor;

            while (!EOC && L(0) != OpenTag && L(0) != CloseTag && !Char.IsWhiteSpace(L(0)))
                ++this.cursor;

            text = (this.cursor == textStart)? String.Empty : GetContent(textStart);

            return true;
        }

        public Boolean GetNormalText(out String content)
        {
            var textStart = this.cursor;

            while (!EOC)
            {
                if (L(0) == OpenTag)
                {
                    Int32 beforeTagCursor = this.cursor;

                    String tagName;
                    if (GetBeginOpenTag(out tagName) || GetBeginCloseTag(out tagName))
                    {
                        this.cursor = beforeTagCursor;
                        break;
                    }
                }
                ++this.cursor;
            }

            var canMatch = (textStart < this.cursor);

            content = canMatch? GetContent(textStart) : null;

            return canMatch;
        }

        public Boolean GetTagName(out String tagName)
        {
            Int32 startTagCursor = this.cursor;

            if (!IsTagChar(L(0)))
                goto Failed;

            Int32 cursorAfterStartTag = ++this.cursor;
            Char c = L(0);

            while (!EOC && (IsTagChar(c) || Char.IsDigit(c) || c == '_' || c == '.' || c == ':' || c == '-'))
            {
                ++this.cursor;
                c = L(0);
            }

            tagName = GetContent(startTagCursor);
            return true;

        Failed:
            tagName = null;
            this.cursor = startTagCursor;
            return false;
        }

        public Boolean SkipEqualSign()
        {
            if (!EOC && CurrentChar == '=')
            {
                ++this.cursor;
                return true;
            }
            else
                return false;
        }

        public Boolean SkipCompleteEndOpenTag()
        {
            var saveCursor = this.cursor;

            SkipWhiteSpaces();

            if (ValidLength(2) && Read() == CloseElementSign && Read() == CloseTag)
                return true;
            else
            {
                this.cursor = saveCursor;
                return false;
            }
        }

        public Boolean SkipEndTag()
        {
            SkipWhiteSpaces();

            if (!EOC && L(0) == CloseTag)
            {
                ++this.cursor;
                return true;
            }
            else
                return false;
        }

        public void SkipWhiteSpaces()
        {
            while (!EOC && Char.IsWhiteSpace(this.content, this.cursor))
                ++this.cursor;
        }

        internal Boolean IsOpenTag()
        {
            return !EOC && L(0) == OpenTag;
        }

        Char CurrentChar
        {
            get { return this.content[this.cursor]; }
        }

        Boolean IsTagChar(Char c)
        {
            return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
        }
    }
}
